package network;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;

import network.messages.KrakenClientMsg;
import network.messages.KrakenServerMsg;

import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;

import bdd.sql.server.bean.ServerCentral;
import bdd.sql.server.bean.ServerSite;
import bdd.sql.server.hibernate.HibernateUtil;

import tools.AppletConstant;
import tools.ConfParser;
import tools.FileUtils;
import tools.StringUtils;

/**
 * Client TCP.
 * @author Guillaume Poças & Jean-François Augé
 */
public class KrakenClient {

	//private static final int DEFAULT_PORT_NUMBER = 18000;
	//local
	//private static final String PATH_CONF_FILE = "C:\\Documents and Settings\\Guillaume\\workspace\\vsm_M2\\conf\\server.conf";
	//private static final String PATH_CONF_FILE = "C:\\Users\\B182JS\\workspace\\vsm\\conf\\server.conf";
	private static final String PATH_CONF_FILE = AppletConstant.SRV_CONF_FILE;
	
	//serv web
	//private static final String PATH_CONF_FILE = "C:\\Serveur Java\\server.conf";
	//serv ubuntu
	//private static final String PATH_CONF_FILE = "/etc/kraken/server.conf";
	
	private static final String DEFAULT_PORT = "DEFAULT_PORT";
		
	private Socket socket;
	private BufferedReader in;
	private PrintWriter out;
	private String servName;
	private String final_answer;
	private ArrayList<String> message_list;
	private boolean finished;
	private byte[] saved_data; 
	
	private BigInteger B;
	
	/**
	 * Constructeur.
	 * @param msg
	 * @param nameServer
	 */
	public KrakenClient(ArrayList<String> msg, String nameServer){
		this.socket = null;
		this.in = null;
		this.out = null;
		this.servName = nameServer;
		this.final_answer = "";
		this.message_list = msg;
		this.finished = false;
	}
	
	/**
	 * Se connecter au serveur TCP.
	 */
	public void connect() {
		try {
			ConfParser parser = new ConfParser();
			try {
				parser.parse(new File(PATH_CONF_FILE)); //Applet.Constant ?
			} catch (IOException e) {
				e.printStackTrace();
			}
			
			int default_port_number = new Integer(parser.getValue(DEFAULT_PORT));
			
			socket = new Socket(servName, default_port_number);
			in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			out = new PrintWriter(socket.getOutputStream(),true);   
			//System.out.println("Connecté à "+servName+":"+DEFAULT_PORT_NUMBER);
		} catch (UnknownHostException e1) {
			e1.printStackTrace();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
	}
	
	/**
	 * Envoyer un message au serveur TCP.
	 * Attente bloquante d'une réponse.
	 * @param msg
	 * @return
	 */
	public String sendMessage(String msg) {
		String answer = "";
		//envoie le message au serveur
		out.println(msg);
		out.flush();
		try{
			//réceptionne la réponse
			answer = in.readLine();
			//System.out.println("Reponse du serveur"+answer);
	    } catch (IOException e2){
	    	System.out.println("Read failed "+e2);
	    	System.exit(1);
	    }
		
        return answer;
	}
	
	/**
	 * Envoyer un message au serveur TCP.
	 * @param msg
	 */
	public void sendMessage_noAnwser(String msg) {
		//envoie le message au serveur
		out.println(msg);
		out.flush();
	}
	
	/**
	 * Démarre la communication avec le serveur.
	 */
	public void communicate() {
		int i = 0;
		String state = "";
		String msgCmd[];
		while(!finished){
			System.out.println("i = "+i+" et size "+message_list.size());
			System.out.println(message_list.get(i));
			state = sendMessage(message_list.get(i));
			System.out.println("rep serv : "+state); //Debug
			msgCmd = state.split("  ");
			
			//traitements intermédiaires
			//envoie de fichier
			if(msgCmd[0].equalsIgnoreCase(KrakenServerMsg.WAITING_FILE.toString())){
				if(msgCmd.length == 3){
					FileUtils.tcpSendFile(socket, msgCmd[1], msgCmd[2]);
					
					//reçoit la fin d'attente du serveur
					try {
						state = in.readLine();
						msgCmd = state.split("  ");
					} catch (IOException e) {
						e.printStackTrace();
					}
					
					//envoie la fin de l'envoie du fichier
					if(msgCmd[0].equalsIgnoreCase(KrakenServerMsg.END_WAITING_FILE.toString())){
						state = sendMessage(KrakenClientMsg.FILE_SENDED.makeCommand());
						msgCmd = state.split("  ");
					}
				}
			}
			
			//distribution des classes de chiffrement, serv web vers global
			else if(msgCmd[0].equalsIgnoreCase(KrakenServerMsg.PATH_ENCRYPTION_DISTRIBUTION.toString())){
				if(msgCmd.length == 4){
					if(msgCmd[1].equalsIgnoreCase("GLOBAL")){
						//1 = GLOBAL & 2 = chemin serv web des fichiers & 3 = chemin serv global où copier les fichiers
						String save_msg = message_list.get(message_list.size()-1);
						int j = i+1;
						File[] files = FileUtils.listFiles(msgCmd[2]);
						
						message_list.remove(j);
						message_list.add(KrakenClientMsg.CLEAN_FOLDER.makeCommand(" "+msgCmd[3]));
						for(File file : files){
							j++;
							//if(j == message_list.size())	
							message_list.add(KrakenClientMsg.COPY_FILE.makeCommand(" "+file.getAbsolutePath(), " "+msgCmd[3]+file.getName()));
						}
	
						Session session = HibernateUtil.getSession();
						ServerCentral server = (ServerCentral) session.createCriteria(ServerCentral.class).
								add(Restrictions.like("globserv_ip", servName)).list().get(0);
						for(ServerSite site : server.getSiteServers())
							message_list.add(KrakenClientMsg.ENCRYPTION_DISTRIBUTION.makeCommand(" LOCAL", " "+site.getSiteserv_ip()," "+msgCmd[3]));
							//=> reaction à ce message = 1ère partie de celui là sans celle là
						message_list.add(save_msg);
					}
					else if(msgCmd[1].equalsIgnoreCase("LOCAL")){
						//1 = LOCAL & 2 = chemin serv global des fichiers & 3 = chemin serv local où copier les fichiers
						String save_msg = message_list.get(message_list.size()-1);
						int j = i+1;
						File[] files = FileUtils.listFiles(msgCmd[2]);
						
						message_list.remove(j);
						message_list.add(KrakenClientMsg.CLEAN_FOLDER.makeCommand(" "+msgCmd[3]));
						for(File file : files){
							j++;
							//if(j == message_list.size())	
							message_list.add(KrakenClientMsg.COPY_FILE.makeCommand(" "+file.getAbsolutePath(), " "+msgCmd[3]+file.getName()));
						}
						message_list.add(save_msg);
					}
				}
			}
			
			else if(msgCmd[0].equalsIgnoreCase(KrakenServerMsg.PATH_ENCRYPTION_PARAMETER.toString())){
				if(msgCmd.length == 4){
					//1 = nom param & 2 = répertoire des classes serv local & 3 = rep du parameter
					String save_msg = message_list.get(message_list.size()-1);
					int j = i+1;
					File[] files = FileUtils.listFiles(msgCmd[2]);
					
					message_list.remove(j);
					message_list.add(KrakenClientMsg.CLEAN_FOLDER.makeCommand(" "+msgCmd[3]));
					for(File file : files){
						j++;
						if(file.getName().equalsIgnoreCase(msgCmd[1]))
							message_list.add(KrakenClientMsg.COPY_FILE.makeCommand(" "+file.getAbsolutePath(), " "+msgCmd[3]));
					}
					message_list.add(save_msg);
				}
			}
			
			else if(msgCmd[0].equalsIgnoreCase(KrakenServerMsg.DH_ACK.toString())) {
				System.out.println("ok");
			}
			
			// protocole diffie-hellman
			else if(msgCmd[0].equalsIgnoreCase(KrakenServerMsg.SEND_B.toString())) {
				// stocker B
				if(msgCmd.length == 2)
					B = new BigInteger(msgCmd[1]);
			}
			
			// récupération du jeton d'authentification
			else if(msgCmd[0].equalsIgnoreCase(KrakenServerMsg.SEND_GENERATED_AUTHENTIFICATION_TOKEN.toString())){
				if(msgCmd.length == 2){
					saved_data = StringUtils.stringToBytes(msgCmd[1]);
				}
			}
			
			
			//bon déroulement de fin
			if(msgCmd[0].equalsIgnoreCase(KrakenServerMsg.END_SERVER.toString())){
				this.final_answer = state;
				finished = true;
			}
			//problème
			else if(msgCmd[0].equalsIgnoreCase(KrakenServerMsg.TIMEOUT.toString())){
				this.final_answer = KrakenServerMsg.TIMEOUT.makeCommand(" "+message_list.get(i));
				finished = true;
			}
			else if(msgCmd[0].equalsIgnoreCase(KrakenServerMsg.ERROR.toString())){
				this.final_answer = KrakenServerMsg.ERROR.makeCommand(" "+message_list.get(i));
				finished = true;
			}
			else if(msgCmd[0].equalsIgnoreCase(KrakenServerMsg.UNKNOWN_COMMAND.toString())){
				this.final_answer = KrakenServerMsg.UNKNOWN_COMMAND.makeCommand(" "+message_list.get(i));
				finished = true;
			}
			//pas de fin "normale" = pas de CLIENT_END envoyé au serveur...
			else if(i == message_list.size()){
				this.final_answer = KrakenServerMsg.ERROR.makeCommand(" NO_END_SENT");
				finished = true;
			}
			i++;
		}
	}
	
	public ArrayList<String> getMessages(){ return this.message_list;	}
	public String getFinalAnswer(){ return this.final_answer;	}
	
	public byte[] getSaved_data() {	return saved_data;	}
	public void setSaved_data(byte[] saved_data) {	this.saved_data = saved_data;	}

	public BigInteger getB() { return B; }
	public void setB(BigInteger b) { B = b; }

}
